<template>
    <a-layout style="height: 100%;" id="components-layout-demo-custom-trigger">
        <a-layout-sider v-model:collapsed="collapsed" :trigger="null" collapsible>
            <div class="MenuBox">
                <div class="logo" />
                <div class="MenuLayout">
                    <Menu />
                </div>
            </div>
        </a-layout-sider>
        <a-layout>
            <a-layout-header style="background: #fff; padding: 0">
                <div class="HeaderContnet">
                    <menu-unfold-outlined v-if="collapsed" class="trigger" @click="() => (collapsed = !collapsed)" />
                    <menu-fold-outlined v-else class="trigger" @click="() => (collapsed = !collapsed)" />
                    <div class="Title">{{ title }}</div>
                    <div class="HeaderButtonBox">
                        <slot name="LayoutHeader"></slot>
                    </div>
                </div>
            </a-layout-header>
            <a-layout-content class="PageContent">
                <div class="TabBox">
                    <a-tabs size="small" v-model:activeKey="UseTabs.ActiveKey" type="editable-card" hide-add
                        @edit="handleTabsEdit">
                        <a-tab-pane v-for="tab in UseTabs.Mapping" :key="tab.key" :tab="tab.title" />
                    </a-tabs>
                </div>
                <div class="Content">
                    <div class="Loading" v-if="UIStore.Mapping[$route.path]">
                        <a-spin />
                    </div>
                    <RouterView v-slot="{ Component }">
                        <keep-alive :exclude="exclude">
                            <component :is="formatCompontentInstance(Component, $route.path)" :key="$route.path" />
                        </keep-alive>
                    </RouterView>
                </div>
            </a-layout-content>
        </a-layout>
    </a-layout>
</template>
<script setup>
import { nextTick, ref, watch, h, provide, inject, onMounted, onUnmounted } from 'vue';
import { useRoute, useRouter } from 'vue-router';
import Menu from './components/Menu.vue'
import { UseTabsStore } from './store/Tabs'
import { UseMenuStore } from './store/Menu';
import { UseUIStore } from './store/UI';


const SetShowLoadingState = inject('SetShowLoadingState', () => { });
onMounted(() => {
    // 进入layout 页面， 主动禁用 App 页面的loading
    SetShowLoadingState(false)
})
onUnmounted(() => {
    // 离开页面时，再启用loading
    SetShowLoadingState(true)
})


// 实例化页面路由
const Route = useRoute();
const Router = useRouter();

// 加载状态管理
const UseTabs = UseTabsStore();
const MenuStore = UseMenuStore();
const UIStore = UseUIStore();

// keepAlive 的缓存 排除列表
const exclude = ref([]);

// 展开收起状态
const collapsed = ref(false);

// layout 页面的标题
const title = ref('');

// keepAlive 的缓存
const keepAliveCache = new Map();

// keepAlive 可清除页面缓存核心方法
const formatCompontentInstance = (Component, path) => {
    let wrapper
    if (keepAliveCache.has(path)) {
        // 如果有, 返回对应的窜存
        wrapper = keepAliveCache.get(path)
    } else {
        // 由于 页面大多数 使用 setup 方法建立，大多数不需要写 name
        // 并且 重用页面是 name 会重复
        // 故 自建页面的 name 来实现页面基于 path 的唯一缓存名称
        wrapper = {
            name: path,
            render() {
                return h(Component)
            },
            Component,
        }
        // 将缓存添加到 缓存 对象中
        keepAliveCache.set(path, wrapper);
    };
    return h(wrapper)
}

// 当标签页面关闭时
function handleTabsEdit(path, action) {
    if (action === 'remove') {
        // 删除 tab 页面的缓存 以便页面能顺利触发 onUnmounted(页面注销) 方法
        // 返回的 _path , 如果关闭的页面 有前一个页签 则打开
        // 没有返回 undefined
        const NextPath = UseTabs.DeleteMapping(path);
        // 将关闭的页面添加到 
        exclude.value.push(path)
        // 清除页面自建缓存
        keepAliveCache.delete(path);
        // 跳转到对应页面
        Router.push(NextPath || import.meta.env.VITE_TAB_DEFAULT_PATH);
    }
}

// 当路由切换时
watch(() => Route, (value) => {
    // 清除 keepAlive 不缓存页面记录,以便页面顺利打开
    exclude.value = [];
    if (value) {
        nextTick(() => {
            // 去菜单目录中查找对应 path 的页面标题
            const label = MenuStore.GetMappingLabel(value.path);
            // 如果没找到, 使用 router 的 meta 中的 标题属性
            const _title = label || value.meta.title
            // 给 tabs 的缓存记录添加属性
            // 由于 tab 的存储结构为 对象（mapping） 类型
            // 所以不用考虑页面被重复打开的问题
            UseTabs.SetMapping(value.path, {
                key: value.path,
                title: _title,
            });
            // 给 layout 页面 的 标题赋值
            title.value = _title;
            if (_title) {
                // 如果标题存在, 则将浏览器 tab 页 的标题修改掉
                document.title = _title + '-' + import.meta.env.VITE_TITLE;
            } else {
                // 如果标题不存在, 使用全局环境配置的默认标题
                document.title = import.meta.env.VITE_TITLE;
            }
            // 切换 tab 页签的焦点状态
            UseTabs.ActiveKey = value.path;
        })
    }
}, { deep: true, immediate: true });

// 当Tab 切换时,将路由切换到对应页面
watch(() => UseTabs.ActiveKey, (val) => {
    if (Route.path != val) {
        Router.push(val);
    }
})

</script>
<style scoped lang="less">
.PageContent {
    position: relative;
    padding: 8px;

    .TabBox {
        user-select: none;
    }

    .Content {
        position: absolute;
        background-color: #fff;
        left: 8px;
        right: 9px;
        bottom: 0;
        top: 44px;
        padding: 8px;
        border-left: 1px solid rgba(5, 5, 5, 0.06);
        border-right: 1px solid rgba(5, 5, 5, 0.1);

        .Loading {
            position: absolute;
            width: 100%;
            height: 100%;
            z-index: 1000;
            top: 0;
            left: 0;
            background-color: rgba(180, 180, 180, 0.5);
            display: flex;
            align-items: center;
            justify-content: center;
        }
    }
}

.MenuBox {
    width: 100%;
    height: 100%;
    overflow: hidden;
    position: relative;

    .MenuLayout {
        position: absolute;
        top: 64px;
        bottom: 0;
        width: 100%;
        overflow-y: scroll;
        padding-bottom: 100px;
    }
}

.HeaderContnet {
    display: flex;
    align-items: center;
    box-shadow: 0 0 3px 1px rgba(0, 0, 0, 0.1);

    .Title {
        font-size: 18px;
    }

    .HeaderButtonBox {
        padding: 0 16px;
        flex: 1;
        height: 100%;
    }
}

#components-layout-demo-custom-trigger .trigger {
    font-size: 18px;
    line-height: 64px;
    padding: 0 24px;
    cursor: pointer;
    transition: color 0.3s;
}


#components-layout-demo-custom-trigger .trigger:hover {
    color: #1890ff;
}

#components-layout-demo-custom-trigger .logo {
    height: 32px;
    background: rgba(255, 255, 255, 0.3);
    margin: 16px;
}

.site-layout .site-layout-background {
    background: #fff;
}
</style>